/* Implements hashtable abstract data type. */

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include "hashtable.h"
#include "linked_list.h"
static const unsigned int primes[] = {
53, 97, 193, 389,
769, 1543, 3079, 6151,
12289, 24593, 49157, 98317,
196613, 393241, 786433, 1572869,
3145739, 6291469, 12582917, 25165843,
50331653, 100663319, 201326611, 402653189,
805306457, 1610612741
};

const int prime_table_length = sizeof(primes)/sizeof(primes[0]);
const float max_load_factor = 0.60;


struct hashtable_data_t {
  int table_size;
  linked_list_t *linked_lists;
  hashtable_pfhashfunction_t *hash_function;
};

hashtable_t
hashtable_create(int min_size, hashtable_pfhashfunction_t hash_fn){
  hashtable_t h = (hashtable_t)malloc(sizeof(struct hashtable_data_t));
  int i=0;
  int table_size=min_size;
  for(i=0; i< prime_table_length;i++){
    if (primes[i] >= min_size){
      table_size=primes[i];
      break;
    }
  }
  h->table_size = table_size;
  h->linked_lists = (linked_list_t*)malloc(sizeof(linked_list_t)*table_size); 
  h->hash_function = hash_fn;
  return h;
}

void 
hashtable_set_hash_function(hashtable_t h, hashtable_pfhashfunction_t pf){
  assert(h!=NULL && pf!=NULL);
  h->hash_function = pf;
}
void 
hashtable_add(hashtable_t h, hashtable_key_t k, hashtable_value_t v){
  assert(h!=NULL);
  int table_size = h->table_size;
  int index = (h->hash_function(k))%table_size;
  linked_list_t linked_list = *(h->linked_lists+index);
  if (linked_list==NULL){
    linked_list=linked_list_create();
    *(h->linked_lists+index)=linked_list;
  }
  linked_list_append(linked_list, k , v);
}

hashtable_value_t 
hashtable_lookup(hashtable_t h, hashtable_key_t k){
  assert(h!=NULL);
  int table_size = h->table_size;
  int index = (h->hash_function(k))%table_size;
  linked_list_t linked_list = *(h->linked_lists+index);
  if (linked_list==NULL){
    return NULL;
  }else{
    return linked_list_key_search(linked_list,k);
  }
}

boolean_t 
hashtable_is_present(hashtable_t h, hashtable_key_t k){
  assert(h!=NULL);
  int table_size = h->table_size;
  int index = (h->hash_function(k))%table_size;
  linked_list_t linked_list = *(h->linked_lists+index);
  if (linked_list==NULL){
    return FALSE;
  }else{
    return (linked_list_key_search(linked_list,k)!=NULL);
  }
}

void 
hashtable_remove(hashtable_t h, hashtable_key_t k){
  assert(h!=NULL);
  int table_size = h->table_size;
  int index = (h->hash_function(k))%table_size;
  linked_list_t linked_list = *(h->linked_lists+index);
  if (linked_list==NULL){
    return;
  }else{
    linked_list_remove(linked_list,k);
    if (linked_list_size(linked_list) == 0){
      *(h->linked_lists+index)=NULL;
    }
  }
}

/* private */
static
boolean_t
show_one(linked_list_element_t k,linked_list_element_t e, linked_list_pfapply_closure_t cl) 
{
  printf(" [%d,%d] ",*(int*)k, *(int*)e);
  *(int*)cl = *(int*)cl + 1;
  return TRUE;
}

void
hashtable_debug(hashtable_t h){
  assert(h!=NULL);
  printf("array size = %d\n", h->table_size);
  linked_list_t *cur = h->linked_lists;
  int i=0;
  while(i<h->table_size){
    linked_list_t linked_list = *cur;
    i++;
    //int size = queue_size(linked_list);
    if (linked_list!=NULL){
      int x=0;
      printf("i = %d\n", i);
      linked_list_apply(linked_list, show_one, &x);
      printf("\n");
    }
    cur++;
  }
}

/**
int 
hastable_table_size(hashtable_t h){
  assert(h!=NULL);
  return h->table_size;
  }*/
